﻿using Sidvall.Net;
using Sidvall.WorkItemManager.Data;
using Sidvall.WorkItemManager.Net.Services.V1;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.WorkItemManager.Workflow.WorkItems
{
    public class WebWorkItemProvider : WorkItemProviderBase
    {
        #region Public Members

        public const string ResultParameter = "{Result}";

        #region ExecuteAsync

        public override async Task<WorkItemResult> ExecuteAsync()
        {
            HttpResponse response;
            WebTarget webTarget;

            DeserializeContext();
            var responseCount = 0;
            var errorCount = 0;
            var message = new System.Text.StringBuilder();

            var settings = new Sidvall.Net.HttpClientSettings();
            var client = Sidvall.SystemContext.Current.HttpClientFactory.GetHttpClient(settings);

            if (this.ContextItem.WebTargets != null)
            {
                foreach (var target in this.ContextItem.WebTargets.Items)
                {
                    if (!target.IsEnabled)
                        continue;
                    response = null;
                    var startTime = System.DateTime.UtcNow;
                    if (target.HttpVerb.Equals("GET", System.StringComparison.OrdinalIgnoreCase))
                    {
                        response = await client.GetAsync(target.WebAddress).ConfigureAwait(false);
                    }
                    else if (target.HttpVerb.Equals("POST", System.StringComparison.OrdinalIgnoreCase))
                    {
                        response = await client.PostAsync(target.WebAddress, target.Content).ConfigureAwait(false);
                    }
                    else if (target.HttpVerb.Equals("PUT", System.StringComparison.OrdinalIgnoreCase))
                    {
                        response = await client.PutAsync(target.WebAddress, target.Content).ConfigureAwait(false);
                    }
                    else if (target.HttpVerb.Equals("DELETE", System.StringComparison.OrdinalIgnoreCase))
                    {
                        response = await client.DeleteAsync(target.WebAddress).ConfigureAwait(false);
                    }
                    var webResult = (from o in this.ExecutionContextItem.WebResults.Items
                                     where o.WebAddress.Equals(target.WebAddress, System.StringComparison.OrdinalIgnoreCase)
                                     select o).FirstOrDefault();
                    if (webResult == null)
                    {
                        webResult = new WebResult()
                        {
                            WebAddress = target.WebAddress,
                        };
                        if (this.ExecutionContextItem.WebExecutionContextId != null)
                            webResult.WebExecutionContextId = this.ExecutionContextItem.WebExecutionContextId.Value;
                        this.ExecutionContextItem.WebResults.Items.Add(webResult);
                    }
                    if (response != null)
                    {
                        webResult.StatusCode = response.StatusCode;
                        if ((response.Content != null) && (response.Content.Headers != null))
                            webResult.ContentLength = response.Content.Headers.ContentLength;
                        else
                            webResult.ContentLength = null;
                        webResult.ResponseTime = System.DateTime.UtcNow.Subtract(startTime);
                        if (!response.IsSuccessStatusCode)
                        {
                            if (message.Length > 0)
                            {
                                message.AppendLine();
                                message.AppendLine();
                            }
                            message.Append(target.WebAddress + ": " + response.StatusCode.ToString());
                            errorCount += 1;
                        }
                        responseCount += 1;
                    }
                    else
                    {
                        webResult.StatusCode = null;
                        webResult.ContentLength = null;
                        webResult.ResponseTime = null;
                    }
                }
            }

            // Remove obsolete results
            var iMax = this.ExecutionContextItem.WebResults.Items.Count;
            for (int i = iMax - 1; i >= 0; i--)
            {
                var webResult = this.ExecutionContextItem.WebResults.Items[i];
                if (this.ContextItem.WebTargets != null)
                {
                    webTarget = (from o in this.ContextItem.WebTargets.Items
                                 where o.WebAddress.Equals(webResult.WebAddress, System.StringComparison.OrdinalIgnoreCase)
                                 select o).FirstOrDefault();
                }
                else
                {
                    webTarget = null;
                }
                if (webTarget == null)
                    this.ExecutionContextItem.WebResults.Remove(webResult);
            }

            // Update sensors
            this.SensorValues.UpdateSensor("ResponseCount", responseCount.ToString(), System.DateTime.UtcNow);
            this.SensorValues.UpdateSensor("ErrorCount", errorCount.ToString(), System.DateTime.UtcNow);
            await UpdateSensorsAsync().ConfigureAwait(false);

            SerializeContext();

            return new WorkItemResult(WorkItemResultType.Ok, message.ToString());
        }

        #endregion
        #region GetSensorsAsync

        public override Task<SensorCollection> GetSensorsAsync()
        {
            SensorCollection sensors;

            sensors = new SensorCollection();
            sensors.Add("ResponseCount", "Number of responses");
            sensors.Add("ErrorCount", "Number of errors");

            return Task.FromResult(sensors);
        }

        #endregion

        #endregion
        #region Private Members

        private WebContext ContextItem { get; set; }
        private WebExecutionContext ExecutionContextItem { get; set; }

        #region DeserializeContext

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void DeserializeContext()
        {
            this.ContextItem = Sidvall.Serialization.SerializerManager.Current.Deserialize<WebContext>(this.Context);
            this.ExecutionContextItem = null;
            try
            {
                this.ExecutionContextItem = Sidvall.Serialization.SerializerManager.Current.Deserialize<WebExecutionContext>(this.ExecutionContext);
            }
            catch
            {
                this.ExecutionContextItem = null;
            }
            if (this.ExecutionContextItem == null)
                this.ExecutionContextItem = new WebExecutionContext();
            if (this.ExecutionContextItem.WebResults == null)
                this.ExecutionContextItem.WebResults = new WebResultCollectionItem();
        }

        #endregion
        #region SerializeContext

        private void SerializeContext()
        {
            this.ExecutionContext = Sidvall.Serialization.SerializerManager.Current.SerializeObject(this.ExecutionContextItem);
        }

        #endregion

        #endregion
    }
}
